package cn.gov.cbrc.east.task;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.concurrent.Callable;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cn.gov.cbrc.core.util.PropertiesUtil;
import cn.gov.cbrc.east.util.FileTruncate;
import cn.gov.cbrc.east.util.FileUtil;
import cn.gov.cbrc.east.util.StringUtil;
import cn.gov.cbrc.east.verify.FormulaParse;
import cn.gov.cbrc.east.verify.ErrorResults;
import cn.gov.cbrc.east.verify.S_Column;
import cn.gov.cbrc.east.verify.S_Formula;
import cn.gov.cbrc.east.verify.S_Table;

/**
 * 项目名称：EastCheck<br>
 * *********************************<br>
 * <P>类名称：Task</P>
 * *********************************<br>
 * <P>类描述：对分割文件进行校验task</P>
 * 创建人：王川<br>
 * 创建时间：Feb 19, 2016 10:46:14 AM<br>
 * 修改人：王川<br>
 * 修改时间：Feb 19, 2016 10:46:14 AM<br>
 * 修改备注：<br>
 * @version 1.0<br>    
 */
@SuppressWarnings("unchecked")
public class Task implements Callable<FileTruncate> {
	private static final Logger logger = LogManager.getLogger(Task.class);
	private FileTruncate fileTruncate;
	
	private long truncateSize = 1024*1024*Integer.parseInt(PropertiesUtil.getProperty("secondTruncateSize"));
	//错误级别
	private String[] errorLevel = {"Error","Error","Warning","Warning","Error","Error","Error","Warning","Warning","Error","Error","Warning"}; 
	private S_Table table;
	
	private ErrorResults result;
	/**文件碎片数量**/
	private int truncateNum;
	public Task(FileTruncate fileTruncate,S_Table table,ErrorResults result,int truncateNum){
		this.fileTruncate = fileTruncate;
		this.table = table;
		this.result = result;
		this.truncateNum = truncateNum;
	}
	
	public void run() throws Exception {
		validate();
	}
	
	/**
	 * <p>方法描述:分割文件校验 </p>
	 * <p>方法备注: </p>
	 * <p>创建人：王川</p>
	 * <p>创建时间：Feb 19, 2016 10:42:36 AM</p>
	 * @throws Exception 
	 *
	 */
	public FileTruncate validate() throws Exception{
		//记录每个分割文件总行数
		int row = 1;
		//记录分割文件校验成功条数
		int _succRows = 0;
		//记录分割文件校验失败条数
		int _failRows = 0;
		String taskId = fileTruncate.getFileName()+"."+fileTruncate.get_index();
		JobLogFactory.delete(taskId);
		Logger logger2 = JobLogFactory.createLogger(taskId);
		List<FileTruncate> _sublist = FileUtil.truncateFileToSubTask(fileTruncate, truncateSize);
		RandomAccessFile accessFile = null;
		FileChannel InChannel = null;
		BufferedReader reader = null;
		ByteArrayInputStream byteStream = null;
		ByteBuffer byteBuffer = null;
		try {
			String line = null;
			accessFile = new RandomAccessFile(fileTruncate.get_filePath(),"r");
			InChannel = accessFile.getChannel();
			for(FileTruncate truncate:_sublist){
				byteBuffer = ByteBuffer.allocate((int) truncate.getFileSize());
				InChannel.position(truncate.get_startPosition());
				InChannel.read(byteBuffer);
				byteBuffer.flip();
				byteStream = new ByteArrayInputStream(byteBuffer.array());
				reader = new BufferedReader(new InputStreamReader(byteStream,"GBK"));
				//TODO reader.readLine方法这个需要测试(windows 和linux不一样)
				while((line=reader.readLine())!= null){
					//错误信息
					StringBuffer errorMsg = new StringBuffer();
					//标志这一条是否校验通过
					boolean rowChecked = true;
					List<String> list = StringUtil.splitStr(line);
					//1、首先判断table字段个数是否相等 ERROE级别划分
					if(list.size() == 0){
						rowChecked = false;
						errorMsg.append("["+errorLevel[0]+"]分割字段异常，请确认英文引号[\"]使用是否符合规范；");
					}else{
						if(list.size() == table.get_columns().size()){
							//2、判断每个字段属性是否有校验公式，如果有则进行公式验证
							int col = 0;
							for(S_Column column:table.get_columns()){
								if(null != column.get_verifies()){
									//标记每个属性是否校验通过
									boolean flag = false;
									//3、对每个属性进行表达式验证
									for(S_Formula formula:column.get_verifies()){
										flag = FormulaParse.parseExpr(formula.get_formula(), formula.get_formulaType(), list.get(col));
										//TODO　根据错误级别判断
										if(!flag){
											rowChecked =false;
											errorMsg.append(buildErrorMsg(col+1, formula.get_formula(), formula.get_formulaType()));
										}
									}
								}
								col++;
							}
						}else{
							rowChecked =false;
							errorMsg.append("["+errorLevel[0]+"]字段个数不对,应有["+table.get_columns().size()+"]个，实际["+list.size()+"]个；");
						}
					}
					
					//这一条数据未校验通过，记录日志
					if(!rowChecked){
						//校验失败数加1
						_failRows++;
						result.set_failRows(result.get_failRows()+1);
						logger2.error("line["+row+"]："+errorMsg.toString());
					}else{
						//校验通过数加1
						_succRows++;
					}
					row++;
				}
				reader.close();
				byteStream.close();
				byteBuffer.clear();
				line = null;
			}
			Double finishedPercent = (result.getFinishedTruncates()+1)*100.00/this.truncateNum;
			String percent = fileTruncate.getFileName()+"已完成："+String.format("%.2f", finishedPercent)+"%\n";
			logger.info(percent);
		}catch(Exception e){
			logger.error(e);
		}finally{
			result.setFinishedTruncates(result.getFinishedTruncates()+1);
			JobLogFactory.stop(taskId);
			//关闭打开的文件流
			try {
				if(null != reader){
					reader.close();
				}
				if(null != byteStream){
					byteStream.close();
				}
				if(null != InChannel){
					InChannel.close();
				}
				if(null != accessFile){
					accessFile.close();
				}
				System.gc();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}
		fileTruncate.set_succRows(_succRows);
		fileTruncate.set_failRows(_failRows);
		fileTruncate.setRow(row-1);
		fileTruncate.setLoggerName(logger2.getName());
		return fileTruncate;
	}
	/**
	 * 获取校验错误详情
	 * @param col 错误列号
	 * @param arg 校验参数
	 * @param type 校验类型
	 * @return
	 */
	String buildErrorMsg(int col ,String arg,int type){
		String level = errorLevel[type];
		if("Error".equals(level)){//设置错误字段数量
			fileTruncate.set_errorFields(fileTruncate.get_errorFields()+1);
		}else if("Warning".equals(level)){//设置警告字段数量
			fileTruncate.set_warningFields(fileTruncate.get_warningFields()+1);
		}
		if(type == 1){//Float校验类型
			return "col["+col+"]["+level+"]应为浮点类型或科学计数法类型；";
		}else if(type == 2){//Cn类型
			return "col["+col+"]["+level+"]应为字符类型且长度为["+arg+"]位；";
		}else if(type == 3){//Cnm类型
			return "col["+col+"]["+level+"]应为字符类型且长度不超过["+arg+"]位；";
		}else if(type == 4){//Int类型
			return "col["+col+"]["+level+"]应为整数类型；";
		}else if(type == 5){//Int类型(限制长度)
			return "col["+col+"]["+level+"]应为整数类型且长度为["+arg+"]位；";
		}else if(type == 6){//Int类型(可选长度)
			return "col["+col+"]["+level+"]应为整数类型且长度不超过["+arg+"]位；";
		}else if(type == 7){//Dw类型
			return "col["+col+"]["+level+"]应为浮点类型且总长度不超过["+(arg+1)+"]位；";
		}else if(type == 8){//Dwd类型
			return "col["+col+"]["+level+"]应为浮点类型且格式符合["+arg+"]；";
		}else if(type == 9){//Sd单选类型
			return "col["+col+"]["+level+"]应为单选类型且选项为["+arg+"]；";
		}else if(type == 10){//Sdm多选类型
			return "col["+col+"]["+level+"]应为多选类型且选项为["+arg+"]；";
		}else if(type == 11){//Nvl非空类型
			return "col["+col+"]["+level+"]不能为空；";
		}else{//未知校验类型
			return "col["+col+"][Error]未知校验类型["+type+"]；";
		}
	}
	@Override
	public FileTruncate call() throws Exception {
		return validate();
	}

}
